package com.mugui.spring.net.baghandle;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.apache.dubbo.config.ReferenceConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.WebAsyncTask;
import org.springframework.web.multipart.MultipartFile;

import com.mugui.spring.base.ModelInterface;
import com.mugui.spring.net.bean.Message;
import com.mugui.spring.net.bean.NetBag;
import com.mugui.spring.net.forward.Forward;
import com.mugui.spring.net.forward.ForwardManager;
import com.mugui.spring.net.session.SessionContext;
import com.mugui.spring.net.sys.SysLogManager;
import com.mugui.spring.util.HTTPUtil;
import com.mugui.spring.util.RedisAccess;
import com.mugui.sql.SqlServer;
import com.mugui.util.Other;

import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.RandomUtil;

@RestController
@CrossOrigin(allowCredentials = "true",originPatterns = "*" )
public class NetHandle implements com.mugui.spring.base.WsHandle {

	@Autowired
	private NetBagModuleManager ModuleMessage = null;
	private static int bag_max = 1024 * 1024 * 10;
	private final byte[] lock = new byte[0];
	int i = 0;
	double index = 10;

	@Autowired
	private RedisAccess redis;

	private ModuleListenerServiceApi moduleListenerServiceApi;

	@Value("${spring.application.name:未命名}")
	private String application_name;

	@RequestMapping(value = { "/" })
	public WebAsyncTask<String> httpHandle(HttpServletRequest request, HttpServletResponse response,
			HttpSession session, MultipartFile[] uploadFile) {
//		Enumeration<String> headerNames = request.getHeaderNames();
//		while (headerNames.hasMoreElements()) {
//			String nextElement = headerNames.nextElement();
//			String header = request.getHeader(nextElement);
//			System.out.println(nextElement + " : " + header);
//		}

		if (System.getProperties().get("system_lock") == null) {
			synchronized (lock) {
				while (System.getProperties().get("system_lock") == null) {
					System.out.println("等待！！！！！！！！");
					Other.sleep(200);
				}
			}
		}
		if (System.currentTimeMillis() - time > 10000) {
			synchronized (NetBagModule.class) {
				if (System.currentTimeMillis() - time > 10000) {
					time = System.currentTimeMillis();
					if ("true".equals(redis.get("is_module_listener"))) {
						is_module_listener = true;
					} else {
						is_module_listener = false;
					}
				}
			}
		}
		TempCallable<String> callable = new TempCallable<String>() {

			@Override
			public String call() throws Exception {
				try {
					NetBag bag = new NetBag();
					this.bag = bag;
					bag.setType(NetBag.TYPE_HTTP);
					bag.setCode(200);
					String header = request.getHeader("remote-host");
					if (StringUtils.isNotBlank(header))
						bag.setFrom_host(header);
					else {
						bag.setFrom_host(request.getRemoteHost());
					}
					bag.setFrom_port(request.getRemotePort());
					bag.setHost(request.getServerName());
					bag.setPort(request.getServerPort());
					bag.setTimestamp(System.currentTimeMillis() + "");
					if (request.getParameter("func") != null) {
						bag.setFunc(request.getParameter("func"));
						bag.setData(request.getParameter("data"));
						bag.setHash(request.getParameter("hash"));
						bag.setSession(request.getParameter("session"));
						bag.setServer_type(request.getParameter("server_type"));
						Map<String, String[]> parameterMap = request.getParameterMap();
						Iterator<Entry<String, String[]>> iterator = parameterMap.entrySet().iterator();
						while (iterator.hasNext()) {
							Entry<String, String[]> next = iterator.next();
							if (next.getValue() != null && next.getValue().length > 0)
								bag.get().put(next.getKey(), next.getValue()[0]);
						}
					} else {
						InputStream inputStream = request.getInputStream();
						ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
						byte[] bs = new byte[1024];
						int len = 0;
						int max_len = 0;
						while ((len = inputStream.read(bs)) > -1) {
							outputStream.write(bs, 0, len);
							max_len += len;
							if (max_len > bag_max) {
								outputStream.close();
								inputStream.close();
								bag.setData(Message.error("limit " + bag_max));
								bag.setRet_data(null);
								bag.setHost(null);
								bag.setPort(null);
								bag.setFrom_host(null);
								bag.setFrom_port(null);
								bag.setTimestamp(null).setServer_type(null);
								return bag.toString();
							}
						}
						String body = outputStream.toString("UTF-8");

						outputStream.close();
						inputStream.close();
						if (StringUtils.isBlank(body)) {
							bag.setRet_data(null);
							bag.setHost(null);
							bag.setPort(null);
							bag.setFrom_host(null);
							bag.setFrom_port(null);
							bag.setTimestamp(null).setServer_type(null);
							bag.setData(Message.error("参数错误"));
							return bag.toString();
						}
						NetBag temp = NetBag.newBean(NetBag.class, body);
						if (temp == null) {

							bag.setRet_data(null);
							bag.setHost(null);
							bag.setPort(null);
							bag.setFrom_host(null);
							bag.setFrom_port(null);
							bag.setTimestamp(null).setServer_type(null);
							bag.setData(Message.error("参数错误"));
							return bag.toString();
						}
						bag.InitBean(temp.get());
						bag.get().putAll(temp.get());
					}
					if (!session.getId().equals(bag.getSession())) {
						SessionContext.DelSession(SessionContext.getSession(bag.getSession()));
					}
					HttpSession session2 = null;
					SessionContext.AddSession(session2 = session);
					bag.setSession(session.getId());
//					System.out.println(
//							"接收" + bag.getFunc() + ":" + bag.getFrom_host() + ":" + bag.getFrom_port() + "->" + bag.getData());
					NetBag tempbag = resolveNetBag(bag, session2, uploadFile);
					if (tempbag == null)
						throw new RuntimeException(bag + "");

					tempbag.setRet_data(null);
					tempbag.setHost(null);
					tempbag.setPort(null);
					tempbag.setFrom_host(null);
					tempbag.setFrom_port(null);
					tempbag.setTimestamp(null).setServer_type(null);
					return tempbag.toString();
				} catch (Exception e) {
					if (is_module_listener) {
						initModuleListenerServiceApi();
						if (bag.getFunc() != null)
							moduleListenerServiceApi.listener(application_name, bag, "通信错误", e);
					}
					e.printStackTrace();
				} finally {
					SessionContext.DelLocalSession();
					SqlServer.reback();
				}
				return new NetBag().toString();
			}

		};
		WebAsyncTask<String> task = new WebAsyncTask<>(callable);
		task.onTimeout(new Callable<String>() {
			@Override
			public String call() throws Exception {
				NetBag bag = NetBag.newBean(NetBag.class, callable.bag);
				bag.setCode(503);
				bag.setData(Message.error("服务器忙"));

				bag.setRet_data(null);
				bag.setHost(null);
				bag.setPort(null);
				bag.setFrom_host(null);
				bag.setFrom_port(null);
				bag.setTimestamp(null).setServer_type(null);

				// throw new TimeoutException();
				return bag.toString();
			}
		});

		return task;

	}

	private void initModuleListenerServiceApi() {
		if (moduleListenerServiceApi == null) {
			synchronized (NetHandle.class) {
				if (moduleListenerServiceApi == null) {
					ReferenceConfig<ModuleListenerServiceApi> referenceConfig = new ReferenceConfig<>();
					referenceConfig.setVersion("");
					referenceConfig.setInterface(ModuleListenerServiceApi.class);
					referenceConfig.setGeneric(false);
					moduleListenerServiceApi = referenceConfig.get();
				}
			}
		}
	}

	public String WsHandle(NetBag bag) {
		try {
			bag.setType(NetBag.TYPE_WS);
			bag.setHash(Other.MD5(bag.getHash() + System.currentTimeMillis() + RandomUtil.randomInt()));
			HttpSession session = SessionContext.getSession(bag.getSession());
			bag = resolveNetBag(bag, session, null);
			if (bag == null)
				return null;
			bag.setRet_data(null);
			bag.setHost(null);
			bag.setPort(null);
			bag.setFrom_host(null);
			bag.setFrom_port(null);
			bag.setTimestamp(null).setServer_type(null);

			return bag == null ? null : bag.toString();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return null;
	}

	private ConcurrentHashMap<String, Integer> isLock = new ConcurrentHashMap<>();

	@Autowired
	private Environment env;
	private static final TimedCache<String, byte[]> HASH_MAP_CACHE = new TimedCache<>(60000 * 60 * 2);
	static {
		HASH_MAP_CACHE.schedulePrune(60000);
	}

	/**
	 * 解析NetBag 到相应的处理位置
	 * 
	 * @param file
	 * 
	 * @throws Exception
	 */
	public NetBag resolveNetBag(NetBag bag, HttpSession session, MultipartFile[] uploadFile) throws Exception {
		if (bag == null) {
			bag = new NetBag();
		}
		if (session == null) {
			bag.setCode(503);
			bag.setData(Message.error("用户登录状态已过期"));
			return bag;
		}
		if (StringUtils.isBlank(bag.getFunc()) || bag.getFunc().split("[.]").length < 2
				|| StringUtils.isBlank(bag.getHash())) {
			bag.setCode(503);
			bag.setData(Message.error("参数错误"));
			return bag;
		}
		String key = bag.getSession() + ":" + bag.getHash();
		synchronized (HASH_MAP_CACHE) {
			byte[] bs = HASH_MAP_CACHE.get(key, false);
			if (bs != null) {
				bag.setCode(503);
				bag.setData(Message.error("消息回环"));
				return bag;
			}
			HASH_MAP_CACHE.put(key, new byte[0]);
		}

		String server_name = bag.getServer_type();
		if (server_name == null) {
			String property = env.getProperty("mugui.spring.server_name");
			if (StringUtils.isBlank(property)) {
				server_name = "default";
			} else
				server_name = property;
			bag.setServer_type(server_name);
		}
		ModelInterface modelInterface = ModuleMessage.get(bag.getFunc().substring(0, bag.getFunc().lastIndexOf(".")));
		if (modelInterface == null) {
			modelInterface = ModuleMessage.get(bag.getFunc());
		}
		if (modelInterface == null) {
			NetBag handleHttpNetBag = handleHttpNetBag(bag, session, uploadFile);
			return handleHttpNetBag;
		}
		long time = 0;
		if (is_module_listener) {
			time = System.currentTimeMillis();
		}

		NetBag ret = (NetBag) handleNetBag(modelInterface, bag, uploadFile);

		ret.setRet_data(null);
		if (is_module_listener) {
			initModuleListenerServiceApi();
			moduleListenerServiceApi.time(application_name, bag.getFunc(), System.currentTimeMillis() - time);
		}

		return ret;
	}

	@Autowired
	private ProcessorManager processor;

	@Autowired
	private SysLogManager dataManager;

	/**
	 * 处理网络请求的bag
	 * 
	 * @author 木鬼
	 * @param modelInterface 包处理器
	 * @param bag            包
	 * @param uploadFile     文件
	 * @return
	 * @throws Exception
	 */
	private NetBag handleNetBag(ModelInterface modelInterface, NetBag bag, MultipartFile[] uploadFile)
			throws Exception {
		processor.init();
		if (uploadFile != null) {
			bag.get().put(NetBag.STEALTH_UPLOAD_FILE, uploadFile);
		}
		// 分布式消息
		NetBag handleAddForward = processor.handleAddForward(bag);
		if (handleAddForward != null) {
			handleAddForward.setRet_data(null);
			return handleAddForward;
		}
		// 过滤器
		NetBag handleAddFilter = processor.handleAddFilter(bag);
		if (handleAddFilter != null) {
			handleAddFilter.setRet_data(null);
			return handleAddFilter;
		}

		Message data = null;
		try {
			NetBag handleAddCache = processor.handleAddCache(bag);
			if (handleAddCache != null) {
				handleAddCache.setRet_data(null);
				return handleAddCache;
			}

			if (ProcessorManager.listener_sys_info) {
				dataManager.NetbagLog().info(application_name, bag);
			}
			data = (Message) modelInterface.invokeFunction("runFunc", bag);
			if (ProcessorManager.listener_sys_info) {
				dataManager.NetbagLog().info(application_name, bag, data);
			}
			processor.saveCache(data, bag);
			bag.setCode(200);
			bag.setData(data);
			return bag;
		} catch (Exception e) {
			e.printStackTrace();
			if (ProcessorManager.listener_sys_error) {
				dataManager.NetbagLog().error(application_name, bag, e);
			}
			bag.setCode(503);
			bag.setData(Message.error("数据传输错误"));
			return bag;
		} finally {
			processor.handleAddListener(bag, data);
		}

	}

	private static long time = 0;
	private static boolean is_module_listener = false;

	@Autowired
	ForwardManager forwardManager = null;

	public void unLock(Forward forward) {
		isLock.put(forward.url(), 0);
	}

	private NetBag handleHttpNetBag(NetBag bag, HttpSession session, MultipartFile[] uploadFile) {
		forwardManager = ModuleMessage.getModelManager(ForwardManager.class);
		Forward forward = (Forward) forwardManager.get(bag.getFunc());
		if (forward == null) {
			bag.setCode(503);
			bag.setData(Message.error("模块未找到"));
			return bag;
		}
		HashMap<String, String> map = new HashMap<String, String>();
		String ForwardSession = (String) session.getAttribute("ForwardSession");
		if (ForwardSession == null)
			ForwardSession = bag.getSession();
		map.put("Cookie", "SESSION=" + Base64.encode(ForwardSession) + "");
		String str = null;
		Integer integer = isLock.get(forward.url());
		if (integer == null)
			integer = 0;
		if (integer != 0 && integer < 10)
			isLock.put(forward.url(), integer + 1);
		if (integer <= 5) {
			if (uploadFile != null) {
				str = HTTPUtil.post(forward.url(), map, bag.get(), uploadFile);
			} else
				str = HTTPUtil.post(forward.url(), map, bag.get().toString());
		}
		if (str == null) {
			NetBag newBag = new NetBag();
			newBag.setCode(503);
			newBag.setData(Message.error("该功能繁忙，请稍后重试"));
			isLock.put(forward.url(), 1);
		} else {
			unLock(forward);
		}
		NetBag newBag = NetBag.newBean(NetBag.class, str);
		if (newBag.getSession() == null || !newBag.getSession().equals(ForwardSession)) {
			session.setAttribute("ForwardSession", newBag.getSession());
		}
		newBag.setSession(session.getId());
		newBag.setFrom_host(bag.getFrom_host());
		newBag.setFrom_port(bag.getFrom_port());
		newBag.setHost(bag.getHost());
		newBag.setPort(bag.getPort());
		return newBag;
	}

	private abstract class TempCallable<T> implements Callable<T> {
		protected NetBag bag;

	}

}
